/* Defines String::compose(fmt, arg...) for easy, i18n-friendly
 * composition of strings.
 *
 * Version 1.0.
 *
 * Copyright (c) 2002 Ole Laursen <olau@hardworking.dk>.
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public License
 * as published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301
 * USA.
 */

//
// Basic usage is like
//
//   std::cout << String::compose("This is a %1x%2 matrix.", rows, cols);
//
// See http://www.cs.aau.dk/~olau/compose/ or the included README.compose for
// more details.
//

#ifndef STRING_COMPOSE_H
#define STRING_COMPOSE_H

#include <sstream>
#include <string>
#include <list>
#include <map> // for multimap

namespace StringPrivate
{
// the actual composition class - using string::compose is cleaner, so we
// hide it here
class Composition
{
public:
  // initialize and prepare format string on the form "text %1 text %2 etc."
  explicit Composition( std::string fmt );

  // supply an replacement argument starting from %1
  template < typename T >
  Composition& arg( const T& obj );

  // compose and return string
  std::string str() const;

private:
  std::ostringstream os;
  int arg_no;

  // we store the output as a list - when the output string is requested, the
  // list is concatenated to a string; this way we can keep iterators into
  // the list instead of into a string where they're possibly invalidated on
  // inserting a specification string
  typedef std::list< std::string > output_list;
  output_list output;

  // the initial parse of the format string fills in the specification map
  // with positions for each of the various %?s
  typedef std::multimap< int, output_list::iterator > specification_map;
  specification_map specs;
};

// helper for converting spec string numbers
inline int
char_to_int( char c )
{
  switch ( c )
  {
  case '0':
    return 0;
  case '1':
    return 1;
  case '2':
    return 2;
  case '3':
    return 3;
  case '4':
    return 4;
  case '5':
    return 5;
  case '6':
    return 6;
  case '7':
    return 7;
  case '8':
    return 8;
  case '9':
    return 9;
  default:
    return -1000;
  }
}

inline bool
is_number( int n )
{
  switch ( n )
  {
  case '0':
  case '1':
  case '2':
  case '3':
  case '4':
  case '5':
  case '6':
  case '7':
  case '8':
  case '9':
    return true;

  default:
    return false;
  }
}


// implementation of class Composition
template < typename T >
inline Composition&
Composition::arg( const T& obj )
{
  os << obj;

  std::string rep = os.str();

  if ( !rep.empty() )
  { // manipulators don't produce output
    for ( specification_map::const_iterator i = specs.lower_bound( arg_no ),
                                            end = specs.upper_bound( arg_no );
          i != end;
          ++i )
    {
      output_list::iterator pos = i->second;
      ++pos;

      output.insert( pos, rep );
    }

    os.str( std::string() );
    // os.clear();
    ++arg_no;
  }

  return *this;
}

inline Composition::Composition( std::string fmt )
  : arg_no( 1 )
{
  std::string::size_type b = 0, i = 0;

  // fill in output with the strings between the %1 %2 %3 etc. and
  // fill in specs with the positions
  while ( i < fmt.length() )
  {
    if ( fmt[ i ] == '%' && i + 1 < fmt.length() )
    {
      if ( fmt[ i + 1 ] == '%' )
      { // catch %%
        fmt.replace( i, 2, "%" );
        ++i;
      }
      else if ( is_number( fmt[ i + 1 ] ) )
      { // aha! a spec!
        // save string
        output.push_back( fmt.substr( b, i - b ) );

        int n = 1; // number of digits
        int spec_no = 0;

        do
        {
          spec_no += char_to_int( fmt[ i + n ] );
          spec_no *= 10;
          ++n;
        } while ( i + n < fmt.length() && is_number( fmt[ i + n ] ) );

        spec_no /= 10;
        output_list::iterator pos = output.end();
        --pos; // safe since we have just inserted a string>

        specs.insert( specification_map::value_type( spec_no, pos ) );

        // jump over spec string
        i += n;
        b = i;
      }
      else
        ++i;
    }
    else
      ++i;
  }

  if ( i - b > 0 ) // add the rest of the string
    output.push_back( fmt.substr( b, i - b ) );
}

inline std::string
Composition::str() const
{
  // assemble string
  std::string str;

  for ( output_list::const_iterator i = output.begin(), end = output.end();
        i != end;
        ++i )
    str += *i;

  return str;
}
}

// now for the real thing(s)
namespace String
{
// a series of functions which accept a format string on the form "text %1
// more %2 less %3" and a number of templated parameters and spits out the
// composited string
template < typename T1 >
inline std::string
compose( const std::string& fmt, const T1& o1 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 );
  return c.str();
}

template < typename T1, typename T2 >
inline std::string
compose( const std::string& fmt, const T1& o1, const T2& o2 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 ).arg( o2 );
  return c.str();
}

template < typename T1, typename T2, typename T3 >
inline std::string
compose( const std::string& fmt, const T1& o1, const T2& o2, const T3& o3 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 ).arg( o2 ).arg( o3 );
  return c.str();
}

template < typename T1, typename T2, typename T3, typename T4 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 );
  return c.str();
}

template < typename T1, typename T2, typename T3, typename T4, typename T5 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 ).arg( o6 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 ).arg( o6 ).arg( o7 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 ).arg( o2 ).arg( o3 ).arg( o4 ).arg( o5 ).arg( o6 ).arg( o7 ).arg(
    o8 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8,
  typename T9 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8,
  const T9& o9 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 )
    .arg( o2 )
    .arg( o3 )
    .arg( o4 )
    .arg( o5 )
    .arg( o6 )
    .arg( o7 )
    .arg( o8 )
    .arg( o9 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8,
  typename T9,
  typename T10 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8,
  const T9& o9,
  const T10& o10 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 )
    .arg( o2 )
    .arg( o3 )
    .arg( o4 )
    .arg( o5 )
    .arg( o6 )
    .arg( o7 )
    .arg( o8 )
    .arg( o9 )
    .arg( o10 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8,
  typename T9,
  typename T10,
  typename T11 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8,
  const T9& o9,
  const T10& o10,
  const T11& o11 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 )
    .arg( o2 )
    .arg( o3 )
    .arg( o4 )
    .arg( o5 )
    .arg( o6 )
    .arg( o7 )
    .arg( o8 )
    .arg( o9 )
    .arg( o10 )
    .arg( o11 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8,
  typename T9,
  typename T10,
  typename T11,
  typename T12 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8,
  const T9& o9,
  const T10& o10,
  const T11& o11,
  const T12& o12 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 )
    .arg( o2 )
    .arg( o3 )
    .arg( o4 )
    .arg( o5 )
    .arg( o6 )
    .arg( o7 )
    .arg( o8 )
    .arg( o9 )
    .arg( o10 )
    .arg( o11 )
    .arg( o12 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8,
  typename T9,
  typename T10,
  typename T11,
  typename T12,
  typename T13 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8,
  const T9& o9,
  const T10& o10,
  const T11& o11,
  const T12& o12,
  const T13& o13 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 )
    .arg( o2 )
    .arg( o3 )
    .arg( o4 )
    .arg( o5 )
    .arg( o6 )
    .arg( o7 )
    .arg( o8 )
    .arg( o9 )
    .arg( o10 )
    .arg( o11 )
    .arg( o12 )
    .arg( o13 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8,
  typename T9,
  typename T10,
  typename T11,
  typename T12,
  typename T13,
  typename T14 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8,
  const T9& o9,
  const T10& o10,
  const T11& o11,
  const T12& o12,
  const T13& o13,
  const T14& o14 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 )
    .arg( o2 )
    .arg( o3 )
    .arg( o4 )
    .arg( o5 )
    .arg( o6 )
    .arg( o7 )
    .arg( o8 )
    .arg( o9 )
    .arg( o10 )
    .arg( o11 )
    .arg( o12 )
    .arg( o13 )
    .arg( o14 );
  return c.str();
}

template < typename T1,
  typename T2,
  typename T3,
  typename T4,
  typename T5,
  typename T6,
  typename T7,
  typename T8,
  typename T9,
  typename T10,
  typename T11,
  typename T12,
  typename T13,
  typename T14,
  typename T15 >
inline std::string
compose( const std::string& fmt,
  const T1& o1,
  const T2& o2,
  const T3& o3,
  const T4& o4,
  const T5& o5,
  const T6& o6,
  const T7& o7,
  const T8& o8,
  const T9& o9,
  const T10& o10,
  const T11& o11,
  const T12& o12,
  const T13& o13,
  const T14& o14,
  const T15& o15 )
{
  StringPrivate::Composition c( fmt );
  c.arg( o1 )
    .arg( o2 )
    .arg( o3 )
    .arg( o4 )
    .arg( o5 )
    .arg( o6 )
    .arg( o7 )
    .arg( o8 )
    .arg( o9 )
    .arg( o10 )
    .arg( o11 )
    .arg( o12 )
    .arg( o13 )
    .arg( o14 )
    .arg( o15 );
  return c.str();
}
}


#endif // STRING_COMPOSE_H
